|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Shared.Collections;
/// <summary>
/// Helpers for working with <see cref="IIntervalTree{T}"/> instances. Can be retrieved by calling <c>.Extensions</c>
/// on an interval tree instance. This is exposed as a struct instead of extension methods as the type inference
/// involved here is too complex for C# to handle (specifically using a <c>TIntervalTree</c> type), which would make
/// ergonomics extremely painful as the callsites would have to pass three type arguments along explicitly.
/// </summary>
internal readonly struct IntervalTreeAlgorithms<T, TIntervalTree>(TIntervalTree tree) where TIntervalTree : IIntervalTree<T>
{
public ImmutableArray<T> GetIntervalsThatMatch<TIntrospector, TIntervalTester>(
int start, int length, in TIntrospector introspector, in TIntervalTester intervalTester)
where TIntrospector : struct, IIntervalIntrospector<T>
where TIntervalTester : struct, IIntervalTester<T, TIntrospector>
{
using var result = TemporaryArray<T>.Empty;
tree.FillWithIntervalsThatMatch(start, length, ref result.AsRef(), in introspector, in intervalTester, stopAfterFirst: false);
return result.ToImmutableAndClear();
}
public ImmutableArray<T> GetIntervalsThatOverlapWith<TIntrospector>(
int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
return GetIntervalsThatMatch(start, length, in introspector, default(Tests<TIntrospector>.OverlapsWithIntervalTester));
}
public ImmutableArray<T> GetIntervalsThatIntersectWith<TIntrospector>(
int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
return GetIntervalsThatMatch(start, length, in introspector, default(Tests<TIntrospector>.IntersectsWithIntervalTester));
}
public ImmutableArray<T> GetIntervalsThatContain<TIntrospector>(
int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
return GetIntervalsThatMatch(start, length, in introspector, default(Tests<TIntrospector>.ContainsIntervalTester));
}
public void FillWithIntervalsThatOverlapWith<TIntrospector>(
int start, int length, ref TemporaryArray<T> builder, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
tree.FillWithIntervalsThatMatch(start, length, ref builder, in introspector, default(Tests<TIntrospector>.OverlapsWithIntervalTester), stopAfterFirst: false);
}
public void FillWithIntervalsThatIntersectWith<TIntrospector>(
int start, int length, ref TemporaryArray<T> builder, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
tree.FillWithIntervalsThatMatch(start, length, ref builder, in introspector, default(Tests<TIntrospector>.IntersectsWithIntervalTester), stopAfterFirst: false);
}
public void FillWithIntervalsThatContain<TIntrospector>(
int start, int length, ref TemporaryArray<T> builder, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
tree.FillWithIntervalsThatMatch(start, length, ref builder, in introspector, default(Tests<TIntrospector>.ContainsIntervalTester), stopAfterFirst: false);
}
public bool HasIntervalThatIntersectsWith<TIntrospector>(
int position, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
return HasIntervalThatIntersectsWith<TIntrospector>(position, 0, in introspector);
}
public bool HasIntervalThatIntersectsWith<TIntrospector>(
int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
return tree.Any(start, length, in introspector, default(Tests<TIntrospector>.IntersectsWithIntervalTester));
}
public bool HasIntervalThatOverlapsWith<TIntrospector>(
int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
return tree.Any(start, length, in introspector, default(Tests<TIntrospector>.OverlapsWithIntervalTester));
}
public bool HasIntervalThatContains<TIntrospector>(
int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
return tree.Any(start, length, in introspector, default(Tests<TIntrospector>.ContainsIntervalTester));
}
public static bool Contains<TIntrospector>(T value, int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
var otherStart = start;
var otherEnd = start + length;
var thisSpan = introspector.GetSpan(value);
var thisStart = thisSpan.Start;
var thisEnd = thisSpan.End;
// TODO(cyrusn): This doesn't actually seem to match what TextSpan.Contains does. It doesn't specialize empty
// length in any way. Preserving this behavior for now, but we should consider changing this.
if (length == 0)
{
return thisStart <= otherStart && otherEnd < thisEnd;
}
return thisStart <= otherStart && otherEnd <= thisEnd;
}
private static bool IntersectsWith<TIntrospector>(T value, int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
var otherStart = start;
var otherEnd = start + length;
var thisSpan = introspector.GetSpan(value);
var thisStart = thisSpan.Start;
var thisEnd = thisSpan.End;
return otherStart <= thisEnd && otherEnd >= thisStart;
}
private static bool OverlapsWith<TIntrospector>(T value, int start, int length, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
var otherStart = start;
var otherEnd = start + length;
var thisSpan = introspector.GetSpan(value);
var thisStart = thisSpan.Start;
var thisEnd = thisSpan.End;
// TODO(cyrusn): This doesn't actually seem to match what TextSpan.OverlapsWith does. It doesn't specialize empty
// length in any way. Preserving this behavior for now, but we should consider changing this.
if (length == 0)
return thisStart < otherStart && otherStart < thisEnd;
var overlapStart = Math.Max(thisStart, otherStart);
var overlapEnd = Math.Min(thisEnd, otherEnd);
return overlapStart < overlapEnd;
}
private static class Tests<TIntrospector>
where TIntrospector : struct, IIntervalIntrospector<T>
{
public readonly struct ContainsIntervalTester : IIntervalTester<T, TIntrospector>
{
public bool Test(T value, int start, int length, in TIntrospector introspector)
=> Contains(value, start, length, in introspector);
}
public readonly struct IntersectsWithIntervalTester : IIntervalTester<T, TIntrospector>
{
public bool Test(T value, int start, int length, in TIntrospector introspector)
=> IntersectsWith(value, start, length, in introspector);
}
public readonly struct OverlapsWithIntervalTester : IIntervalTester<T, TIntrospector>
{
public bool Test(T value, int start, int length, in TIntrospector introspector)
=> OverlapsWith(value, start, length, in introspector);
}
}
}
|